#pragma once
#include <unordered_map>
#include <string>
#include <atomic>
#include <functional>
#include "packet.h"
#include "caller.h"
#include "server.h"

namespace KRPC {

class Service
{
public:
	Service(Server &_server) : server(_server) {}

	template<typename F>
	void bind(std::string funcname, F &&func)
	{
		functions.emplace(std::move(funcname), std::bind(Caller<decay_t<F>>(), 
			wrapper(std::forward<F>(func)), std::placeholders::_1));
	}

	template<typename F, typename Self>
	void bind(std::string funcname, F &&func, Self *self)
	{
		functions.emplace(std::move(funcname), std::bind(Caller<decay_t<F>, Self>(), 
			wrapper(std::forward<F>(func)), self, std::placeholders::_1));
	}

	template<typename F, typename Self, typename T, typename ... Args>
	void bind(std::string funcname, F &&func, Self *self, T && t, Args&&... args)
	{
		using FUNCTION_TYPE = typename function_traits<decay_t<F>>::std_function_type;
		functions.emplace(std::move(funcname), std::bind(Caller<FUNCTION_TYPE>(), 
			FUNCTION_TYPE(std::bind(std::forward<F>(func), self, std::forward<T>(t), std::forward<Args>(args)...)), 
			std::placeholders::_1));
	}

	void run(SubcribeCallback subCallback = nullptr)
	{
		server.run([this](Packet &packet){
			const MsgHead *head = packet.getHead();
			auto it = functions.find(head->name);
			if (it != functions.end()) {
				packet.setMode(RPC_REP);
				if (!it->second(packet)) {	//调用RPC函数
					packet.setMode(RPC_NOT_MATCH);
					packet.pack();
				}
			} else {	//说明没找到对应的函数,直接回复
				packet.setMode(RPC_NOT_FOUND);
				packet.pack();
			}
		}, std::move(subCallback));
	}

	template<typename ... Args>
	void publish(const char *topic, Args&&... args)
	{
		auto len = strlen(topic);
		if (topic == nullptr || len > MAX_RPCNAME_LEN - 1)
			return;
		Packet packet(1024, sizeof(MsgHead));
		MsgHead *head = packet.getHead();
		std::memset(head, 0, sizeof(MsgHead));
		head->mode = RPC_PUB;
		head->ver = 0;	//发布内容默认不进行校验
		memcpy(head->name, topic, len);
		head->name[len] = '\0';
		packet.pack(std::tuple<Args...>(std::forward<Args>(args)...));
		server.publish(std::move(packet));
	}
	template<typename ... Args>
	void publish(const std::string &topic, Args&&... args) {publish(topic.c_str(), std::forward<Args>(args)...);}

private:
	Server &server;
	std::unordered_map<std::string, std::function<bool(Packet &)>> functions;
};

}
